home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Libraries / SAT 2.4.0 / SAT / Demo ƒ / OffscreenToys SAT demo ƒ / OffScreenToysSAT.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-23  |  24.1 KB  |  783 lines  |  [TEXT/KAHL]

  1. /* --------- OFFSCREEN TOYS with SAT --------- */
  2. /* by Ingemar Ragnemalm 1994 */
  3. /* ported to Think C by Charles Brunet */
  4.  
  5. /* Offscreen Toys is a nice little demo I made to make a SAT-like demo with        */
  6. /* complete    source code, independent of any libraries (except Apple's). Now,    */
  7. /* this should be easier to do with SAT, right? Well, partially so, but while    */
  8. /* adapting it to SAT, I ran into a minor flaw that SAT had (and doesn't have    */
  9. /* from version 2.1 and up), namely that drawing took place after moving        */
  10. /* sprites, but before checking for collisions, which didn't look too good in    */
  11. /* programs where sprites bounce off each other. After fixing this flaw, I        */
  12. /* would say that the SAT version is indeed a bit better than the independent    */
  13. /* version. The result is quite a bit faster and with asynch sound.                */
  14. /*                                                                                */
  15. /* As yet another SAT demo, what does it give us?                                */
  16. /* • If you wonder what it costs to have a real event loop, or good collision    */
  17. /* handling, this demo shows that pretty well.                                    */
  18. /* • Demonstrates a moveable window AND fast mode at the same time, with the    */
  19. /* precautions that demands when moving the window. (The window mustn't be        */
  20. /* moved outside the screen, some internal SAT variables – that you otherwise    */
  21. /* should never care about – must be adjusted, and we must stay word-aligned to    */
  22. /*make it work in b/w).                                                            */
  23. /*                                                                                */
  24. /* I don't consider this demo final in any way. Known flaws:                    */
  25. /* • I have made some mistakes in the port-setting. Nothing fatal, I think.        */
  26. /* FIXED.                                                                        */
  27. /* • I don't protect the user from moving the window outside the screen, which    */
  28. /* might be fatal when the SAT blitters are turned on. FIXED.                    */
  29. /* • Like in the "real" Offscreen Toys, there is a bug that causes sprites to    */
  30. /* disappear for a short while, since the position gets negative. No big deal.    */
  31. /* • The code for the marble should be separated from the main program, to make    */
  32. /* the code easier to follow.                                                    */
  33. /* I'll fix those things when I find time and inspiration for it - but you are    */
  34. /* welcome to do it if you want!                                                */
  35.  
  36. #include <SAT.h>
  37.  
  38. /*  --- PART 1: Variables and constants: -------------------------------------- */
  39.  
  40. /* A redefined sprite record:                                                    */
  41. struct OTSprite {
  42.         /* Variables that you should change as appropriate                        */
  43.     short        kind;            /* Used for identification. >0: friend. <0 foe    */
  44.     Point        position;
  45.     Rect        hotRect, hotRect2; /* Tells how large the sprite is; hotRect is    */
  46.                                 /* centered around origo hotRect is set by you.    */
  47.                                 /* hotRect2 is offset to the current position.    */
  48.     FacePtr        face;            /* Pointer to the Face (appearance) to be used.    */
  49.     ProcPtr        task;            /* Callback-routine, called once per frame. If    */
  50.                                 /* task=nil, the sprite is removed.                */
  51.     ProcPtr        hitTask;        /* Callback in collisions.                        */
  52.     ProcPtr        destructTask;/* Called when a sprite is disposed. (Usually nil.)*/
  53.     RgnHandle    clip;        /* Clip region to be used when this sprite is drawn.*/
  54.         /*  SAT variables that you shouldn't change:                            */
  55.     Point        oldpos;        /* The 'task' routine is not allowed to change this!*/
  56.     SpritePtr    next, prev;        /* You may change them in your own sorting        */
  57.                                 /* routine, but be careful if you do.            */
  58.     Rect        r, oldr;        /* Rectangle telling where to draw. Avoid        */
  59.                                 /* messing with it.                                */
  60.     FacePtr        oldFace;        /* Used by RunSAT2                                */
  61.     Boolean        dirty;            /* Used by RunSAT2                                */
  62.         /* Variables for internal use by the sprites. Use as you please. Edit    */
  63.         /* as necessary - this is merely a default set, enough space for most    */
  64.         /* cases - but if you change the size of the record, call SetSpriteSize    */
  65.         /* immediately after initializing (before any sprites are created)!        */
  66.     short        layer;    /*For layer-sorting. When not used for that, use freely.*/
  67.     Point        speed;            /*  Can be used for speed, but not necessarily.    */
  68.     short        mode;            /* Usually used for different modes and/or to    */
  69.                                 /* determine what image to show next.            */
  70.     Point        fixedPos;        /* Fixed-point position                            */
  71.     long        appLong;        /* long for free use by the application.        */
  72. };
  73. typedef struct OTSprite    OTSprite;
  74. typedef    OTSprite        *OTSpritePtr;
  75.  
  76. #define kAppleID        128
  77. #define kFileID            129
  78. #define kMBarHeight         20        /* We assume 20 pixels menu bar for window        */
  79.                                 /* sizing and dragging.                            */
  80. #define kWindID            128        /* Window resource ID                            */
  81. #define kAboutAlertID    128        /* Alert resource ID                            */
  82.  
  83. #define kSpriteNumber      5        /*  Number of moving objects                    */
  84.  
  85. #define kWallBounce          7        /* 1/10-ths of speed kept after wallbounce        */
  86. #define kBallDiameterSquared    (32 * 32)    /* Diameter 32, squared                */
  87.  
  88. #define patID            128
  89.  
  90. #define    kSleep              5        /* Real programs may modify the sleep time        */
  91.                                 /* depending on whether or not they are in the    */
  92.                                 /* front                                        */
  93. /* Trap numbers */
  94. #define _WaitNextEvent    0xA860
  95. #define _GetCIcon        0xAA1E    /* E.g. any Color QuickDraw routine                */
  96. #define k32bQD            0xAB1D
  97. #define _SndPlay        0xA805
  98.  
  99.  
  100. Boolean        gColorQDFlag;        /* True if 32-bit QD exists. If not, we run        */
  101.                                 /* everything in b/w.                            */
  102. Boolean        gHasWNE;            /* True if we can use WaitNextEvent                */
  103. Boolean        gSoundFlag;            /* True if we want sound.                        */
  104. Boolean        gFast;
  105.  
  106. Boolean        gWhoa;                /* True when we want to quit                    */
  107. Boolean        gCollisionFlag;        /* Collisions or not?                            */
  108.  
  109.     /* Menu handles */
  110. MenuHandle    appleMenu, fileMenu;
  111.  
  112.     /* The window we'll be using */
  113. WindowPtr    gWind;
  114.  
  115.     /* Our two offscreens: */
  116. /* GrafPtr    offScreen, backScreen; */
  117.  
  118.     /* A cicn loded as a face */
  119. FacePtr        gCicn;
  120. Handle        kgck;
  121.  
  122. /* Sprite information. In real games, I prefer making a linked list of records,    */
  123. /* like I do in SAT, and a lot more information for each, but here we want it     */
  124. /* *simple*.                                                                    */
  125. /* - position: The positions in local coordinates for the window                */
  126. /* - fixedPos: 16 times position, which gives us fixed-point numbers            */
  127. /* - speed: Speed vectors that is added to fixedPos for every frame                */
  128. /* - r: Rectangles used in drawing, for remembering what part of the screen to    */
  129. /* update                                                                        */
  130. /* Point    position[kSpriteNumber], fixedPos[kSpriteNumber];                    */
  131. /* Point    speed[kSpriteNumber];                                                */
  132. /* Rect        r[kSpriteNumber];                                                    */
  133. /*                                                                                */
  134. /* …but, since this now uses SAT, the linked list is built-in, so all the        */
  135. /* variables above are in the sprite record!                                    */
  136.  
  137. /* A long that shows how big the "bowl" is (assumes circular, not oval, bowl!): */
  138.  
  139. long    gBowlSize;
  140.  
  141. pascal void SetupMarble (OTSpritePtr);
  142. pascal void HandleMarble(OTSpritePtr);
  143. pascal void HitMarble(OTSpritePtr, OTSpritePtr);
  144.  
  145. void    MainLoop(void);
  146. void    OTInit(void);
  147. void    OTOffscreensInit(void);
  148.  
  149.  
  150. /*  --- MAIN PROGRAM BODY: ---------------------------------------------------- */
  151. main()
  152. {
  153.     OTInit();                    /* General initializations                        */
  154.     OTOffscreensInit();            /* Set up the offscreen grafports                */
  155.     InitCursor();                /* Set the cursor to arrow in case it isn't.    */
  156.     SATSetPortScreen();            /* The front window is a good port to use.        */
  157.  
  158. /* Run until quit or click in the close box.                                    */
  159.     do {
  160.         SATSetPortScreen();        /* The front window is a good port to use.        */
  161.         MainLoop();
  162.     }while (!gWhoa);
  163.  
  164. /* No cleanup is necessary here.                                                                    */
  165. /* We could DisposeGWorld, but that isn't necessary when we are quitting.                            */
  166.     SATSoundShutup();
  167. }
  168.  
  169. /*  --- PART 2: Various general, reuseable routines, mostly glue: ------------- */
  170.  
  171. /* BailOut: Emergency exit. We go here on most errors. Real programs report        */
  172. /* what the problem is. You may wish to put a breakpoint in BailOut when        */
  173. /* debugging.                                                                    */
  174.  
  175. // void BailOut(void)
  176. // {
  177. //     SysBeep(1); /* Minimal error message. Use alert in real programs. */
  178. //     ExitToShell();
  179. // }
  180.  
  181.  
  182. /* Several functions deleted since SAT handles what they do. */
  183.  
  184. /*  --- PART 3: Application specific routines: -------------------------------- */
  185.  
  186. /* mouse clicks, keydowns, background tasks and update events: This is where    */
  187. /* all the action is. :-) I include some empty procedures for you to fill in if    */
  188. /* you want to use this demo as application shell.                                */
  189.  
  190. /* Mouse click in window content */
  191.  
  192. void DoMouse (Point where, long modifiers)
  193. {
  194.  
  195. }
  196.  
  197. /* Keydown. */
  198.  
  199. void DoKey (char theKey, long modifiers)
  200. {
  201.  
  202. }
  203.  
  204. /* A rather boring subroutine that moves the sprites i and j away from each        */
  205. /* other. I almost didn't want to put this in, making the demo unnecessarily    */
  206. /* big, but I wanted decent collisions. If anyone can suggest a better (simpler)*/
  207. /* collision handling for circular objects, I'd be happy to put it in. I use a    */
  208. /* line drawing algorithm for it. I'm sure there are better ways. This is of    */
  209. /* questionable value as reuseable code - depends on your application.            */
  210.  
  211. /* Normal signum function (which I don't think is in the libs) */
  212. short Sgn(short x)
  213. {
  214.     if (x > 0)
  215.         return 1;
  216.     else if (x < 0)
  217.         return -1;
  218.     else
  219.         return 0;
  220. }
  221.  
  222. /* I don't think this function exist in C - Charles */
  223. short abs(short x)
  224. {
  225.     return (x > 0) ? x : -x;
  226. }
  227.  
  228. void Separate (OTSpritePtr i, OTSpritePtr j)
  229. {
  230.     Point    initVector, nowVector;
  231.     short    absH, absV;
  232.     short    moveH, moveV;
  233.     short    frac = 0;
  234.  
  235.     initVector.h = i->position.h - j->position.h;
  236.     initVector.v = i->position.v - j->position.v;
  237.     absH = abs(initVector.h);
  238.     absV = abs(initVector.v);
  239.     moveH = Sgn(initVector.h);
  240.     moveV = Sgn(initVector.v);
  241.     if (!moveH) {
  242.         if (!moveV) {
  243.             moveV = 1;
  244.         }
  245.     }
  246.     do {
  247.         if (absH > absV) {
  248.             i->position.h += moveH;
  249.             j->position.h -= moveH;
  250.             frac += absV;
  251.             if (frac > absH) {
  252.                 i->position.v += moveV;
  253.                 j->position.v -= moveV;
  254.                 frac -= absH;
  255.             }
  256.         }
  257.         else {
  258.             i->position.v += moveV;
  259.             j->position.v -= moveV;
  260.             frac += absH;
  261.             if (frac > absV) {
  262.                 i->position.h += moveH;
  263.                 j->position.h -= moveH;
  264.                 frac -= absV;
  265.             }
  266.         }
  267.         nowVector.h = i->position.h - j->position.h;
  268.         nowVector.v = i->position.v - j->position.v;
  269.     } while(!((long)(nowVector.h * nowVector.h) + (long)(nowVector.v * nowVector.v) > kBallDiameterSquared));
  270.     
  271.     i->fixedPos.h = (i->position.h << 4); /* Make fixedPos by shifting in the 4    */
  272.     i->fixedPos.v = (i->position.v << 4); /* binary "decimals"                    */
  273.  
  274.     j->fixedPos.h = (j->position.h << 4); /* Make fixedPos by shifting in        */
  275.     j->fixedPos.v = (j->position.v << 4); /* the 4binary "decimals"            */
  276. }
  277.  
  278.  
  279. /* Split a vector (v1) into one component parallell to another vector            */
  280. /* (direction) and one orthogonal to it.                                        */
  281.  
  282. void SplitVector(Point v1, Point direction, Point *parallell, Point *normal)
  283. {
  284.     long        l2, v1pr;
  285.  
  286.     l2 = direction.h * direction.h + direction.v * direction.v; /* Squared        */
  287.                                 /* length of "direction"                        */
  288.     v1pr = v1.h * direction.h + v1.v * direction.v; /* Scalar product            */
  289.  
  290.     parallell->h = direction.h * v1pr / l2;
  291.     parallell->v = direction.v * v1pr / l2;
  292.     normal->h = v1.h - parallell->h;
  293.     normal->v = v1.v - parallell->v;
  294. }
  295.     
  296. pascal void HandleMarble(OTSpritePtr i)
  297. {
  298.     Point        vector;
  299.  
  300.         /* Modify fixed-point position by speed */
  301.     i->fixedPos.h += i->speed.h;
  302.     i->fixedPos.v += i->speed.v;
  303.  
  304.         /* Make position by shifting away the 4 binary "decimals" */
  305.     if (i->fixedPos.h >= 0)
  306.         i->position.h = (i->fixedPos.h >> 4);
  307.     else
  308.         i->position.h = ((i->fixedPos.h >> 4) | 0xf000);
  309.     if (i->fixedPos.v >= 0)
  310.         i->position.v = (i->fixedPos.v >> 4);
  311.     else
  312.         i->position.v = ((i->fixedPos.v >> 4) | 0xf000);
  313.  
  314.         /* Outside the allowed area? */
  315.     if (i->position.h < 0)
  316.         i->speed.h = abs(i->speed.h) * kWallBounce / 10 + 1;
  317.     if (i->position.v < 0)
  318.         i->speed.v = abs(i->speed.v) * kWallBounce / 10 + 1;
  319.     if (i->position.h + gCicn->iconMask.bounds.right > gSAT.offScreen.port->portRect.right)
  320.         i->speed.h = -abs(i->speed.h) * kWallBounce / 10 - 1;
  321.         if (i->position.v + gCicn->iconMask.bounds.bottom > gSAT.offScreen.port->portRect.bottom)
  322.             i->speed.v = -abs(i->speed.v) * kWallBounce / 10 - 1;
  323.  
  324.         /* Are we in the bowl? If we are, accelerate towards the center. */
  325.     vector.h = i->position.h + 16 - (gSAT.offScreen.port->portRect.right >> 1);
  326.     vector.v = i->position.v + 16 - (gSAT.offScreen.port->portRect.bottom >> 1);
  327.     if ((vector.h * vector.h + vector.v * vector.v) < gBowlSize) {
  328.         i->speed.h -= vector.h / 2;
  329.         i->speed.v -= vector.v / 2;
  330.     }
  331. }
  332.  
  333. pascal void HitMarble(OTSpritePtr i, OTSpritePtr j)
  334. {
  335.     Point        vector;
  336.     long        squaredLength;
  337.     Point        p1, p2, n1, n2, tmpSpeed;
  338.     
  339.     if (!gCollisionFlag)
  340.         return;
  341.  
  342.         /* Find the vector between them */
  343.     vector.h = i->position.h - j->position.h;
  344.     vector.v = i->position.v - j->position.v;
  345.     squaredLength = (long)(vector.h * vector.h) + (long)(vector.v * vector.v);
  346.         /* If it is shorter than the diameter of a ball… */
  347.     if (squaredLength < kBallDiameterSquared) {
  348.         /* Move them away from each other */
  349.         Separate(i, j);
  350.  
  351. /* Swap the speed components that are parallell to "vector" (this allows for    */
  352. /* "touches", very nice and realistic bounces)                                    */
  353.         SplitVector(i->speed, vector, &p1, &n1);
  354.         SplitVector(j->speed, vector, &p2, &n2);
  355.  
  356.         j->speed.h = p1.h + n2.h;
  357.         j->speed.v = p1.v + n2.v;
  358.  
  359.         i->speed.h = p2.h + n1.h;
  360.         i->speed.v = p2.v + n1.v;
  361.  
  362. /* Old Offscreen Toys just swapped the speed, as commented out below. This is    */
  363. /* not as realistic.                                                            */
  364. /*        tmpSpeed = i->speed; */
  365. /*        i->speed = j->speed; */
  366. /*        j->speed = tmpSpeed; */
  367.  
  368. /* Play a sound. SAT is good at playing sounds! */
  369.         if (gSoundFlag)
  370.             SATSoundPlay(kgck, 1, false);
  371.     }
  372. }
  373.  
  374. pascal void SetupMarble(OTSpritePtr i)
  375. {
  376.     i->fixedPos.h = (i->position.h << 4);
  377.     i->fixedPos.v = (i->position.v << 4);
  378.     i->speed.h = Random() % 32;
  379.     i->speed.v = Random() % 32;
  380.     i->task = (ProcPtr)&HandleMarble;
  381.     i->hitTask = (ProcPtr)&HitMarble;
  382.     i->face = gCicn;
  383.     SetRect(&(i->hotRect), 0, 0, 32, 32);
  384. }
  385.  
  386. /* A little routine for setting the forecolor with a single line. */
  387.         
  388. void OTForeColor(short red, short green, short blue)
  389. {
  390.     RGBColor        theColor;
  391.     
  392.     theColor.red = red;
  393.     theColor.green = green;
  394.     theColor.blue = blue;
  395.     RGBForeColor(&theColor);
  396. }
  397.  
  398. void DrawBackground(void)
  399. {
  400.     /* A static variable only used in DrawBackground */
  401.     static PixPatHandle    thePat;
  402.     Rect                r;
  403.     Boolean                mycolorFlag;
  404.     SATPort                savePort;
  405.  
  406.         /* DrawBackground */
  407.     SATGetPort(&savePort);
  408.     SATSetPortBackScreen();
  409.         /* Do some drawing in backScreen. First, we paint a pattern (using a    */
  410.         /* 'ppat'    resource):                                                    */
  411.  
  412.         /* For drawing the background, let's make a local flag that tells us if    */
  413.         /* we shold draw b/w patterns or color ones.                            */
  414.     if (gColorQDFlag)
  415.         mycolorFlag = gSAT.initDepth > 1;
  416.     else
  417.         mycolorFlag = false;
  418.  
  419.     if (mycolorFlag) {
  420.         if (thePat == NULL)
  421.             thePat = GetPixPat(patID);
  422.         PenPixPat(thePat);
  423.     }
  424.     else {
  425.         if (thePat == NULL)
  426.             thePat = (PixPatHandle)GetResource('ppat', patID);
  427.         PenPat(&(*thePat)->pat1Data);
  428.     }
  429.     PaintRect(&gSAT.backScreen.port->portRect);
  430.     PenNormal();
  431.  
  432.         /* Then we draw some circles. */
  433.  
  434.     r = gSAT.backScreen.port->portRect;
  435.     InsetRect(&r, (r.right - r.left) / 8, (r.bottom - r.top) / 8);
  436.                                 /* Tells how big the "bowl" is!                    */
  437.     gBowlSize = (long)(r.right - r.left) * (long)(r.right - r.left) / 4;
  438.     if (mycolorFlag) {
  439.         OTForeColor(-10000, -10000, -10000);
  440.         PaintOval(&r);
  441.     }
  442.     else
  443.         FillOval(&r, &qd.ltGray);
  444.  
  445.     InsetRect(&r, (r.right - r.left) / 8, (r.bottom - r.top) / 8);
  446.     if (mycolorFlag) {
  447.         OTForeColor(-25000, -25000, -25000);
  448.         PaintOval(&r);
  449.     }
  450.     else
  451.         FillOval(&r, &qd.gray);
  452.  
  453.     InsetRect(&r, (r.right - r.left) / 6, (r.bottom - r.top) / 6);
  454.     if (mycolorFlag) {
  455.         OTForeColor(20000, 20000, 20000);
  456.         PaintOval(&r);
  457.     }
  458.     else
  459.         FillOval(&r, &qd.dkGray);
  460.  
  461.     InsetRect(&r, (r.right - r.left) / 5, (r.bottom - r.top) / 5);
  462.     if (gSAT.colorFlag)
  463.         OTForeColor(0, 0, 0);
  464.     PaintOval(&r);
  465.  
  466.     SATSetPortOffScreen();
  467.     CopyBits(&gSAT.backScreen.port->portBits, &gSAT.offScreen.port->portBits, &gSAT.backScreen.port->portRect, &gSAT.backScreen.port->portRect, srcCopy, NULL);
  468.  
  469.     SATSetPort(&savePort);
  470. }
  471.  
  472. /* DoBackground: repeating tasks - this is called repeatedly, after every event    */
  473. /* we get.                                                                        */
  474.  
  475. /* Note: If you are making a really Mac-friendly program, this is where you        */
  476. /* should drive the animation. However, it is hard to get high framerate then,    */
  477. /* since other programs (the Finder included) will process events which will    */
  478. /* make it less smooth.                                                            */
  479.  
  480. void DoBackground(void)
  481. {
  482.     Rect        tmpRect;
  483.     short        i, j;
  484.     Point        vector;
  485.     GDHandle    saveGD;
  486.     GrafPtr        savePort;
  487.     Point        tmpSpeed;
  488.  
  489.     SATRun(gFast); /* Eller konfigurerbart? */ /* What? - Charles */
  490. }
  491.  
  492. /* DoUpdate: handle update events, in this case by copying offScreen to the        */
  493. /* screen (gWind).                                                                */
  494.  
  495. /* Note to beginners: A program without update events processing is not a real    */
  496. /* Mac program! All drawing you do must reach the update event handler in some    */
  497. /* way, or it might be lost, or worse, partially erased, which is really ugly.    */
  498.  
  499. void DoUpdate(void)
  500. {
  501.     GDHandle    saveGD;
  502.     GrafPtr        savePort;
  503.     
  504.     if (SATDepthChangeTest())
  505.         DrawBackground();
  506.     BeginUpdate(gWind);
  507.         SATRedraw();
  508.     EndUpdate(gWind);
  509. }
  510.  
  511. /* DoAppleMenu and DoFileMenu: handle menu selections */
  512.  
  513. void DoAppleMenu (short item)
  514. {
  515.     Str255        str;
  516.     Handle        h;
  517.     SATPort        savePort;
  518.     short        ignore;
  519.  
  520.     if (item == 1) {
  521.         if (Alert(kAboutAlertID, nil) == 1)
  522.             ; /* Ignore result */
  523.     }
  524.     else {
  525.             /* Apple menu other than "About": Code from TransSkel */
  526.         SATGetPort(&savePort);
  527.         GetItem(appleMenu, item, str);
  528.         SetResLoad(false);
  529.         h = GetNamedResource('DRVR', str);
  530.         SetResLoad(true);
  531.         if (h != NULL) {
  532.             ReserveMem(SizeResource(h) + 0x1000);
  533.                                             /* Old intf: ResrvMem, SizeResource */
  534.             ignore = OpenDeskAcc(str);
  535.         }
  536.         SATSetPort(&savePort);
  537.     }
  538. }
  539.     
  540. void DoFileMenu (short item)
  541. {
  542.     long        start, finish, frames;
  543.     
  544.     Str255        str1, str2 = "\p frames/second";
  545.     
  546.     switch (item) {
  547.         case 1:
  548. /* Run animation without event processing until the user clicks the mouse        */
  549. /* Note: This runs the animation at maximum speed. In real programs, we must    */
  550. /* limit the speed with the system clock, e.g. inspect TickCount.                */
  551.             start = TickCount();
  552.             frames = 0;
  553.             while (!Button()) {
  554.                 DoBackground();
  555.                 frames++;
  556.             }
  557.             finish = TickCount();
  558.         /* Concat two strings -- Charles */
  559.             NumToString(frames * 60 / (finish - start), str1);/* convert to Str255*/
  560.             BlockMove(&str2[1], &str1[str1[0] + 1], str2[0]); /* concat */
  561.             str1[0] += str2[0];                            /* Add number of char    */
  562.             SATReportStr(str1);
  563.             break;
  564.             
  565.         case 2: 
  566.             gCollisionFlag = !gCollisionFlag;
  567.             CheckItem(fileMenu, 2, gCollisionFlag);
  568.             break;
  569.             
  570.         case 3: 
  571.             gFast = !gFast;
  572.             CheckItem(fileMenu, 3, gFast);
  573.             break;
  574.             
  575.         case 4: 
  576.             gSoundFlag = !gSoundFlag;
  577.             CheckItem(fileMenu, 4, gSoundFlag);
  578.             break;
  579.             
  580. /* Set the flag that tells the program to quit. */
  581.         case 6: 
  582.             gWhoa = true;
  583.     }
  584. }
  585.  
  586. /*  --- PART 4: Event processing: --------------------------------------------- */
  587.  
  588. /* MenuSelection: Menu selection by mouse or command-key: */
  589.  
  590. void MenuSelection (long whatSelection)
  591. {
  592.     switch (HiWord(whatSelection)) {
  593.         case kAppleID: 
  594.             DoAppleMenu(LoWord(whatSelection));
  595.             break;
  596.  
  597.         case kFileID: 
  598.             DoFileMenu(LoWord(whatSelection));
  599.             break;
  600.     }
  601.     HiliteMenu(0);
  602. }
  603.  
  604. /* MainLoop: get and process events. This is the boring standard part of all    */
  605. /* programs. I prefer using TransSkel to get rid of it. I don't here since I    */
  606. /* want this code to be stand-alone. */
  607.  
  608. void MainLoop(void)
  609. {
  610.     Boolean        hasEvent;
  611.     EventRecord    theEvent;
  612.     char        theKey;
  613.     long        whatSelection;
  614.     short        whichPart;
  615.     WindowPtr    whichWindow;
  616.     Rect        r;
  617.     Point        p, pt = {0x0040, 0x0040};
  618.  
  619. /* Get the next event. Use WaitNextEvent if possible. */
  620.     if (gHasWNE)
  621.         hasEvent = WaitNextEvent(everyEvent, &theEvent, kSleep, NULL);
  622.     else {
  623.         SystemTask();
  624.         hasEvent = GetNextEvent(everyEvent, &theEvent);
  625.     }
  626.  
  627. /* OK, so what happened then? */
  628.     if (hasEvent) {
  629.         switch (theEvent.what) {
  630.             case mouseDown: 
  631.                 whichPart = FindWindow(theEvent.where, &whichWindow);
  632.                 switch (whichPart) {
  633.                     case inMenuBar: 
  634.                         whatSelection = MenuSelect(theEvent.where);
  635.                         MenuSelection(whatSelection);
  636.                         break;
  637.                         
  638.                     case inSysWindow: 
  639.                         SystemClick(&theEvent, whichWindow);
  640.                         break;
  641.                     
  642.                     case inGoAway: 
  643.                         if (TrackGoAway(whichWindow, theEvent.where))
  644.                             gWhoa = true;
  645.                         break;
  646.                         
  647.                     case inDrag: 
  648.                         if ((whichWindow != FrontWindow()) && (!(theEvent.modifiers & cmdKey)))
  649.                             SelectWindow(whichWindow);
  650.                         r = gSAT.bounds;            /* How big is the screen?    */
  651.                         r.top += kMBarHeight;        /*  Skip down past menu bar    */
  652.                         InsetRect(&r, 4, 4);
  653.  
  654. /*  LIMIT THE DRAGGING so no part of the window can get outside the screen! Cut    */
  655. /* down on r depending on where the click is? This is necessary if we use "fast    */
  656. /* mode", that is if we do SATRun(true) or any other operation with custom        */
  657. /* blitters.                                                                    */
  658.                         SetPort(whichWindow);
  659.                         p = theEvent.where;
  660.                         GlobalToLocal(&p);
  661.                         r.bottom -= (whichWindow->portRect.bottom - p.v);
  662.                         r.left += p.h;
  663.                         r.right -= (whichWindow->portRect.right - p.h);
  664.  
  665.                         DragWindow(whichWindow, theEvent.where, &r);
  666.  
  667.                         if (whichWindow == gSAT.wind.port)
  668.                             SATWindMoved();
  669.                         break;
  670.                         
  671.                     case inGrow: 
  672.                             break;  /* Ignored - we don't resize */
  673.                         
  674.                     case inContent: 
  675.                         if (whichWindow != FrontWindow())
  676.                             SelectWindow(whichWindow);
  677.                         else
  678.                         /* Go to application-specific mouse down handling */
  679.                             DoMouse(theEvent.where, theEvent.modifiers);
  680.                         break;
  681.                 }
  682.                 break;
  683.  
  684.             case keyDown:
  685.             case autoKey: 
  686.                 theKey = (char)(theEvent.message & charCodeMask);
  687.                 if (theEvent.modifiers & cmdKey)
  688.                     MenuSelection(MenuKey(theKey));
  689.                 else
  690.                     DoKey(theKey, theEvent.modifiers);
  691.                 break;
  692.                 
  693.             case updateEvt:
  694. /* There's only one window to bother with here, but let's make sure that's the    */
  695. /* one the Mac wants to update.                                                    */
  696.                 if ((WindowPtr)theEvent.message == gWind)
  697.                     DoUpdate();
  698. /* Handle disk inserts like TransSkel. */
  699.             case diskEvt: 
  700.                 if (HiWord(theEvent.message) != noErr) {
  701.                     DILoad();
  702.                     if (!DIBadMount(pt, theEvent.message))
  703.                         ;
  704.                     DIUnload();
  705.                 }
  706.         }
  707.     }
  708.     DoBackground();
  709. }
  710.  
  711. /*  --- PART 5: Initializations: ----------------------------------------- */
  712.  
  713. /* OTInit: Initialize global flags, menus and window */
  714.  
  715. void OTInit(void)
  716. {
  717.     SATInitToolbox();
  718.  
  719.     gHasWNE = SATTrapAvailable(_WaitNextEvent);
  720.     gColorQDFlag = SATTrapAvailable(k32bQD) && SATTrapAvailable(_GetCIcon); /* ??? */
  721.     gWhoa = false;
  722.     gCollisionFlag = false;
  723.         
  724.     gSoundFlag = true;
  725.  
  726. /* What more should I check for? Check with Gestalt instead? */
  727.  
  728.     qd.randSeed = TickCount();    /* Seed the random number generator - TickCount    */
  729.                                 /* is good enough.                                */
  730.  
  731. /* Get the window, a color window if we are going to use color. */
  732.     if (gColorQDFlag)
  733.         gWind = GetNewCWindow(kWindID, NULL, (WindowPtr)-1);
  734.     else
  735.         gWind = GetNewWindow(kWindID, NULL, (WindowPtr)-1);
  736.  
  737. /* Some menus. We could read these from resources. */
  738.     appleMenu = NewMenu(kAppleID, "\p\024");
  739.     AppendMenu(appleMenu, "\pAbout Offscreen Toys SAT…;(-");
  740.     AddResMenu(appleMenu, 'DRVR');            /* Old intf: AddResMenu */
  741.     InsertMenu(appleMenu, 0);            /*  put apple menu at end of menu bar  */
  742.     fileMenu = NewMenu(kFileID, "\pFile");
  743.     AppendMenu(fileMenu, "\pTry max speed;Collisions;Use SAT blitters;Use sound;(-;Quit/Q");
  744.     InsertMenu(fileMenu, 0);            /*  put file menu at end of menu bar  */
  745.     DrawMenuBar();
  746.     CheckItem(fileMenu, 4, gSoundFlag);
  747. }
  748.  
  749. /* OTOffscreensInit: Initialize offscreen grafports (worlds) and draw in them. */
  750.  
  751. void OTOffscreensInit(void)
  752. {
  753.     SATPort        savePort;
  754.     Rect        r;
  755.     short        i;
  756.     SpritePtr    sp;
  757.     
  758.     SATConfigure(false, kNoSort, kForwardOneCollision, 32); /* Only call *one*    */
  759.                                 /* hitTask, not both.                            */
  760.     SATCustomInit(0, 0, &gWind->portRect, gWind, NULL, false, false, false, true, false);
  761.  
  762.     DrawBackground();
  763.  
  764. /* Done drawing! */
  765. /* For your own hacks, consider using a PICT resource and use GetPicture and    */
  766. /* DrawPicture to draw the background. Note that you'll need both a color and    */
  767. /* a b/w picture if you want it to look good in b/w. */
  768.  
  769.     SATSetPortScreen();
  770.  
  771. /* Get the cicn resource */
  772. /* Note: You can, of course, use several cicns and switch between. */
  773.     gCicn = SATGetFace(128);
  774.     kgck = SATGetNamedSound("\pKgck");
  775.  
  776. /* Initialize the sprites: */
  777.  
  778.     for (i = 1; i <= kSpriteNumber; i++)
  779.         sp = SATNewSprite(1, SATRand(gSAT.offScreen.port->portRect.right - 32), (i - 1) * (gSAT.offScreen.port->portRect.bottom - 32) / 5 + SATRand((gSAT.offScreen.port->portRect.bottom - 32) / 5), (TaskPtr)&SetupMarble);
  780.  
  781.     if (SATSoundInitChannels(2) < 2)
  782.         SysBeep(1);
  783. }